package com.softtek.webbrowser;

import java.awt.Desktop;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.SocketAddress;
import java.net.URL;
import java.net.URLConnection;
import java.util.Observable;

import com.softtek.exception.JBException;
/**
 * Manages HTTP calls and content retrieval.
 * @author hector.nevarez
 *
 */
public class UrlManager extends Observable{
	
	private String url;
	private URL urlLocation;
	private URLConnection urlConnection;
	private Object pageContent;
	private Proxy proxy;
	private String contentType;
	private Integer contentLength;
	
	private Boolean loadContent = true;
	
	private static final String[] MIME_TYPES = new String[]{
		".XLS", ".DOC", ".PPT", ".PPS", ".PDF", ".RTF", ".BMP",
		".JPG", ".JPEG", ".GIF", ".WAV", ".MP3", ".WMA", ".WMF",
		".OGG"
	};
	
	
	/**
	 * Class constructor
	 */
	public UrlManager(){
		
		String proxtAdress = JBProperties.getProperties().getProperty("PROXY_ADDRESS");
		Integer proxyPort = Integer.parseInt(JBProperties.getProperties().getProperty("PROXY_PORT"));
		
		if(proxtAdress != null && !proxtAdress.equals("")){
			SocketAddress proxyAddress = new InetSocketAddress( proxtAdress, proxyPort);
			proxy = new Proxy(Proxy.Type.HTTP, proxyAddress);
		}else{
			proxy = Proxy.NO_PROXY;
		}
		
	}
	/**
	 * Loads the content specified in the url {@link URL}
	 * 
	 * @param url
	 * @throws MalformedURLException
	 * @throws IOException
	 */
	private Boolean loadContent(final String url) throws MalformedURLException, IOException, UnhandableContent{
		this.url = url;
		Boolean renderableContentLoaded = false;
		
		urlLocation = new URL(url);
		
		urlConnection = urlLocation.openConnection(proxy);
		
		renderableContentLoaded = handleContent(urlConnection);
		
		setChanged();
		notifyObservers( this.url );
		
		return renderableContentLoaded;
		
	}
	
	/**
	 * Handles the content loaded depending on the MIME type.
	 * 
	 * @param urlConnection
	 * @throws IOException
	 */
	private Boolean handleContent(final URLConnection urlConnection) throws IOException, UnhandableContent{
		
		final InputStream urlResponse = urlConnection.getInputStream();
		contentType = urlConnection.getContentType();
		contentLength = urlConnection.getContentLength();
		Boolean rendereableContentLoaded = false;
		
		if(contentType.startsWith("text/html") ||  contentType.startsWith("application/xml") || contentType.startsWith("text/xml")){
			rendereableContentLoaded = handleTextInput(urlResponse);
		}else{
			handleByteInput(urlResponse);
		}
		
		urlResponse.close();
		
		return rendereableContentLoaded;
	}
	/**
	 * Handles all text content.
	 * 
	 * @param urlResponse
	 * @throws IOException
	 */
	private Boolean handleTextInput( final InputStream urlResponse) throws IOException{
		final BufferedReader bufferReader = new BufferedReader( new InputStreamReader(urlResponse) );
		final StringBuffer responseContent = new StringBuffer(); 			
		Boolean contentLoaded = false;
		
		if(loadContent){
			String contentLine = bufferReader.readLine();
				
			while( contentLine != null){
				contentLine = bufferReader.readLine();
				responseContent.append( contentLine );
			}
			
			contentLoaded = true;
			pageContent = responseContent.toString();
		}else{
			contentLoaded = true;
		}
			
		return contentLoaded;
	}
	/**
	 * Handles all binary content. Known types are open in the respective aplications such as:<br>
	 * <ul>
	 * 	<li>Word.- *.doc, *.rtf</li>
	 * 	<li>Excel.- *.xls</li>
	 *  <li>Power Point.- *.ppt, *.pps</li>
	 *  <li>Acrobat PDF.- *.pdf</li>
	 * </ul>
	 * All other files are automaticaly downloaded to the temp folder as soecified in the browser properties file.
	 * 
	 * 
	 * @param urlResponse
	 * @throws IOException
	 */
	private void handleByteInput( final InputStream urlResponse ) throws IOException, UnhandableContent{
		final File srcFile = new File(url);
		String fileExt = getFileExt(srcFile);
		final File temFile = File.createTempFile("temp", fileExt, new File(JBProperties.getProperties().getProperty("TEMP_DIR")));
		temFile.deleteOnExit();
		
		final FileOutputStream tempFileWriter = new FileOutputStream(temFile);
		int bufferSize = 1024;
		byte[] buffer = new byte[bufferSize];
		
		int totalBytesRead = 0;
		
		while( (totalBytesRead = urlResponse.read(buffer)) != -1 ){
				tempFileWriter.write(buffer, 0, totalBytesRead);
				tempFileWriter.flush();
		}

		tempFileWriter.close();
		
		try{
			openFile(temFile);
		}catch (InterruptedException ie) {
			throw new IOException("Process interrupted:: " + ie.getMessage());
		}
		
	}
	
	/**
	 * Restrives file extension for the content loaded.
	 * 
	 * @param srcFile
	 * @return
	 */
	private String getFileExt(final File srcFile){
		return srcFile.toString().substring(srcFile.toString().lastIndexOf("."));
	}
	/**
	 * Attemps to open known mime type file once it is downloaded. 
	 * 
	 * @param tmpFile
	 * @throws IOException
	 * @throws InterruptedException
	 */
	public void openFile(final File tmpFile) throws IOException, InterruptedException, UnhandableContent{
		
		Desktop desktop = Desktop.getDesktop();
		
		if(!tmpFile.canRead()){
			new File(tmpFile.getParent()).setReadable(true);
			tmpFile.setReadable(true);
		}
		
		if(isMimeTypeKnown(tmpFile)){
			desktop.open(tmpFile);
		}else{
			throw new UnhandableContent("This type of file is not safe to open, please find it on "+ tmpFile.getAbsolutePath() 
						+".\n All files are deleted once the browser exits");						
		}
	}
	/**
	 * Validates for known MIME type file to auto open
	 * @param tmpFile
	 * @return
	 */
	private Boolean isMimeTypeKnown(final File tmpFile){
		String fileExt = getFileExt(tmpFile);
		Boolean known = false;
		
		for(int cc = 0; cc <= MIME_TYPES.length; cc++)
			if(fileExt.equalsIgnoreCase(MIME_TYPES[cc]) ) {
				known = false;
				break;
		}
		
		return known;
	}

	/**
	 * Loads the content of the specified url {@link URL}. 
	 * This is the public wrapper of the private method loadContent().
	 * 
	 * @param url absolute URL of the desired content.
	 * @return HTML content of the specified URL
	 * @throws IOException
	 * @see loadContent
	 */
	public Boolean getContent(final String url) throws IOException, UnhandableContent{
		return loadContent(url);
	}
	public URLConnection getConnection(){
		return urlConnection;
	}
	/**
	 * Retrieves the content type for the current content loaded.
	 * @return String containing the content type.
	 */
	public String getContentType(){
		return contentType;
	}
	/**
	 * Returns the current URL
	 * @return current url specified.
	 */
	public String getUrl(){
		return this.url;
	}
	public Boolean getLoadContent() {
		return loadContent;
	}
	public void setLoadContent(Boolean loadContent) {
		this.loadContent = loadContent;
	}
	public Object getPageContent() {
		return pageContent;
	}
	public Proxy getProxy(){
		return proxy;
	}
	public Integer getContentLength() {
		return contentLength;
	}
}
